package mysql

import (
	"errors"
	"fmt"
	"gitee.com/captials-team/ubdframe/src/common/consts"
	"gitee.com/captials-team/ubdframe/src/domain/configstc"
	"gitee.com/captials-team/ubdframe/src/domain/dto"
	"gitee.com/captials-team/ubdframe/src/domain/dto/paginate"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gitee.com/captials-team/ubdframe/src/domain/models"
	mysqlClients "gitee.com/captials-team/ubdframe/src/infrastructure/clients/mysql"
	"gorm.io/gorm"
)

func NewRbacDao(conf configstc.DBConfig) *RbacDao {
	return &RbacDao{
		db:   mysqlClients.NewGormDB(conf),
		conf: conf,
	}
}

type RbacDao struct {
	db   *gorm.DB
	conf configstc.DBConfig
}

func (dao *RbacDao) Use(db *gorm.DB) interfaces.ItfRbac {
	return &RbacDao{db: db}
}

func (dao *RbacDao) DB() *gorm.DB {
	return dao.db
}

func (dao *RbacDao) UserRoles(uid int64) ([]*models.RbacRole, error) {
	var list []*models.RbacRole
	roleIds, err := dao.UserRoleIds(uid)
	if err != nil {
		return nil, err
	}

	db := dao.db.Model(new(models.RbacRole)).Order("id asc")
	if ret := db.Where("id in (?)", roleIds).Find(&list); ret.Error != nil {
		return list, ret.Error
	}
	return list, nil
}

func (dao *RbacDao) UserPerms(uid int64) ([]*models.RbacPermission, error) {
	var perms []*models.RbacPermission
	permIds, err := dao.UserPermIds(uid)
	if err != nil {
		return nil, err
	}
	if ret := dao.db.Model(new(models.RbacPermission)).Where("id in (?)", permIds).Find(&perms); ret.Error != nil {
		return perms, ret.Error
	}
	return perms, nil
}

func (dao *RbacDao) UserPermIds(uid int64) ([]int64, error) {
	var permIds []int64

	roleIds, err := dao.UserRoleIds(uid)
	if err != nil {
		return nil, err
	}
	if ret := dao.db.Model(new(models.RbacRolePermission)).Where("role_id in (?)", roleIds).Pluck("permission_id", &permIds); ret.Error != nil {
		return permIds, ret.Error
	}
	return permIds, nil
}

// 用户关联角色
func (dao *RbacDao) UserRoleIds(uid int64) ([]int64, error) {
	var roleIds []int64
	if ret := dao.db.Model(new(models.RbacUserRole)).Where("user_id=?", uid).Pluck("role_id", &roleIds); ret.Error != nil {
		return roleIds, ret.Error
	}
	return roleIds, nil
}

func (dao *RbacDao) UserRelateRoles(uid int64, roleIds []int64) error {
	var relates []*models.RbacUserRole
	for _, roleId := range roleIds {
		relates = append(relates, &models.RbacUserRole{
			RoleId: roleId,
			UserId: uid,
		})
	}

	db := dao.db.Begin()

	//批量删除后批量创建
	ret := db.Where("user_id=?", uid).Unscoped().Delete(new(models.RbacUserRole))
	if ret.Error != nil {
		db.Rollback()
		return ret.Error
	}

	ret = db.Create(relates)
	if ret.Error != nil {
		db.Rollback()
		return ret.Error
	}

	db.Commit()
	return nil
}

// SearchRoles 查找角色列表
func (dao *RbacDao) SearchRoles(search *dto.SearchRbacRoleParams, pa *paginate.Pager) ([]*models.RbacRole, *paginate.Pager, error) {
	if search == nil {
		search = &dto.SearchRbacRoleParams{}
	}
	var list []*models.RbacRole
	pa.Correct()

	db := dao.db.Model(new(models.RbacRole)).Omit("describe")

	if search.Domain != "" {
		db = db.Where("domain=?", search.Domain)
	}
	if search.Status > 0 {
		db = db.Where("status=?", search.Status)
	}
	if len(search.Keywords) > 0 {
		keywords := "%" + search.Keywords + "%"
		db = db.Where("name like ?", keywords)
	}
	if ret := db.Count(&pa.Total); ret.Error != nil {
		return list, pa, ret.Error
	}

	if !search.IsAll {
		db = db.Limit(pa.Size).Offset(pa.Offset())
	}
	db = db.Order("status asc").
		Order("sort desc").
		Order("id asc")

	if ret := db.Find(&list); ret.Error != nil {
		return list, pa, ret.Error
	}
	pa.Correct()
	if search.IsAll {
		pa.Size = int(pa.Total)
		pa.PageCount = 1
		pa.Page = 1
	}
	return list, pa, nil
}

// Roles 角色可用列表
func (dao *RbacDao) Roles() ([]*models.RbacRole, error) {
	var list []*models.RbacRole

	db := dao.db.Model(new(models.RbacRole)).
		Where("status", consts.StatusEnable).
		Omit("describe").
		Order("status asc").
		Order("sort desc").
		Order("id asc")
	if ret := db.Find(&list); ret.Error != nil {
		return list, ret.Error
	}
	return list, nil
}

func (dao *RbacDao) QueryRole(id int64) (*models.RbacRole, error) {
	var info models.RbacRole
	ret := dao.db.Model(&info).First(&info, "id = ?", id)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *RbacDao) AddRole(add *models.RbacRole) (int64, error) {
	if add.Id > 0 {
		return 0, fmt.Errorf("not support update")
	}
	ret := dao.db.Model(add).Create(add)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("add fail")
	}

	return add.Id, nil
}

func (dao *RbacDao) UpdateRole(id int64, up *models.RbacRole) (int64, error) {
	if id <= 0 {
		return 0, fmt.Errorf("not find info")
	}
	up.Id = id
	ret := dao.db.Save(up)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no update")
	}
	return ret.RowsAffected, nil
}

func (dao *RbacDao) DeleteRole(id int64) error {
	ret := dao.db.Where("id=?", id).Delete(&models.RbacRole{})
	if ret.Error != nil {
		return ret.Error
	}
	if ret.RowsAffected == 0 {
		return fmt.Errorf("no update")
	}
	return nil
}

func (dao *RbacDao) ResetRoleStatus(id int64, status int) (int64, error) {
	// 根据条件更新
	db := dao.db.Model(&models.RbacRole{})
	ret := db.Where("id = ?", id).Update("status", status)
	return ret.RowsAffected, ret.Error
}

// RolePermIds 角色关联权限
func (dao *RbacDao) RolePermIds(roleId int64, moreRoles ...int64) ([]int64, error) {
	var permIds []int64
	db := dao.db.Model(new(models.RbacRolePermission)).Where("role_id=?", 0) //防止查询所有的数据
	if roleId > 0 {
		db = db.Or("role_id=?", roleId)
	}
	if len(moreRoles) > 0 {
		db = db.Or("role_id in (?)", moreRoles)
	}
	if ret := db.Pluck("permission_id", &permIds); ret.Error != nil {
		return permIds, ret.Error
	}
	return permIds, nil
}

func (dao *RbacDao) RoleRelatePerms(roleId int64, permIds []int64) error {
	var relates []*models.RbacRolePermission
	for _, permId := range permIds {
		relates = append(relates, &models.RbacRolePermission{
			RoleId:       roleId,
			PermissionId: permId,
		})
	}

	db := dao.db

	//批量删除后批量创建
	ret := db.Where("role_id=?", roleId).Unscoped().Delete(new(models.RbacRolePermission))
	if ret.Error != nil {
		return ret.Error
	}

	//重新关联创建
	if len(relates) > 0 {
		ret = db.Create(relates)
		if ret.Error != nil {
			return ret.Error
		}
	}

	return nil
}

// Perms 权限列表
func (dao *RbacDao) Perms() ([]*models.RbacPermission, error) {
	db := dao.db.Model(new(models.RbacPermission))

	var list []*models.RbacPermission

	db = db.Order("sort desc").Order("id asc")
	if ret := db.Find(&list); ret.Error != nil {
		return list, ret.Error
	}
	return list, nil
}

// SimplePerms 权限简易列表
func (dao *RbacDao) SimplePerms() ([]*dto.RbacPermissionSimple, error) {
	db := dao.db.Model(new(models.RbacPermission))

	var list []*dto.RbacPermissionSimple

	db = db.Order("sort desc").Order("id asc")
	if ret := db.Find(&list); ret.Error != nil {
		return list, ret.Error
	}
	return list, nil
}

func (dao *RbacDao) QueryPerm(id int64) (*models.RbacPermission, error) {
	var info models.RbacPermission
	ret := dao.db.Model(&info).First(&info, "id = ?", id)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *RbacDao) AddPerm(add *models.RbacPermission) (int64, error) {
	if add.Id > 0 {
		return 0, fmt.Errorf("not add")
	}
	ret := dao.db.Model(new(models.RbacPermission)).Create(add)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("add fail")
	}

	return add.Id, nil
}

func (dao *RbacDao) BatchAddPerm(batch ...*models.RbacPermission) (int64, error) {
	if len(batch) <= 0 {
		return 0, fmt.Errorf("not add")
	}
	ret := dao.db.Model(new(models.RbacPermission)).Create(batch)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("add fail")
	}

	return ret.RowsAffected, nil
}

func (dao *RbacDao) UpdatePerm(id int64, up *models.RbacPermission) (int64, error) {
	if id <= 0 {
		return 0, fmt.Errorf("not find info")
	}
	up.Id = id
	ret := dao.db.Save(up)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no update")
	}
	return ret.RowsAffected, nil
}

func (dao *RbacDao) DeletePerm(id int64) error {
	ret := dao.db.Where("id=?", id).Delete(&models.RbacPermission{})
	if ret.Error != nil {
		return ret.Error
	}
	if ret.RowsAffected == 0 {
		return fmt.Errorf("no update")
	}
	return nil
}

func (dao *RbacDao) Migrate() (int64, error) {
	return 4, dao.db.AutoMigrate(
		new(models.RbacRole),
		new(models.RbacUserRole),
		new(models.RbacRolePermission),
		new(models.RbacPermission),
	)
}
